import mitt from 'mitt'
import { ref, provide, inject, onBeforeUnmount } from 'vue'
import WorkFlow, {
    registerNode as registerWorkflowNode,
    registerEdge as registerWorkflowEdge
} from '@/utils/workflow'
export const injectGetInstanceKey = Symbol('GetInstance')
export const injectGetGraphKey = Symbol('GetGraph')
export const injectEventOnKey = Symbol('EventOn')
export const injectEventEmitKey = Symbol('EventEmit')
export const injectAppendEventKey = Symbol('AppendEvent')
export const injectModifyEventKey = Symbol('ModifyEvent')
export const injectAppendJudgmentKey = Symbol('AppendJudgment')
export const injectModifyJudgmentKey = Symbol('ModifyJudgment')
import { enumEventModule } from '@/utils/emunEvent'
export function useWorkflow() {
    const startID = '0'
    const endID = '-1'
    const refSketchpad = ref()
    const eventBus = mitt()
    let eventMap = {}

    let workFlowCls = new WorkFlow({
        startID,
        endID,
        nodeShape: 'vue-node-card',
        edgeShape: 'edge-connector'
    })

    /**
     * 注册监听事件
     * @param eventName
     * @param callback
     * @returns
     */
    const registerEvent = (eventName, callback) => {
        eventBus.on(eventName, callback)

        if (!Array.isArray(eventMap[eventName])) {
            eventMap[eventName] = []
        }

        eventMap[eventName].push(() => eventBus.off(eventName, callback))

        return {
            destroy: () => {
                eventBus.off(eventName, callback)
            }
        }
    }

    const destroyEvent = (eventName) => {
        if (eventName && Array.isArray(eventMap[eventName])) {
            eventMap[eventName].forEach((destroy) => {
                destroy?.()
            })

            delete eventMap[eventName]

            return
        }

        Object.keys(eventMap).forEach((key) => {
            eventMap[key].forEach((destroy) => {
                destroy?.()
            })

            delete eventMap[key]
        })
    }

    /**
     * 广播已监听的事件
     * @param eventName
     * @param args
     */
    const emitEvent = (eventName, args) => {
        if (!eventMap[eventName]) {
            console.log(
                `%c尚未创建 [${eventName}] 事件的监听事件`,
                'background-color: #F00; color: #FFF'
            )
            return
        }

        eventBus.emit(eventName, args)
    }

    /**
     * 获取 Workflow 的实例
     * @param cb 应用实例的回调函数
     * @returns
     */
    const getWorkflowCls = (cb) => {
        if (!workFlowCls) {
            return
        }

        return cb(workFlowCls)
    }

    /**
     * 获取 X6 的 Graph 实例
     * @param cb 应用实例的回调函数
     * @returns
     */
    const getGraphCls = (cb) => {
        if (!workFlowCls) {
            return
        }

        return workFlowCls.getCls(cb)
    }

    /**
     * 注册事件节点
     * @param name
     * @param data
     */
    const registerNode = (name, data) => {
        registerWorkflowNode(name, data)
    }

    /**
     * 注册链接线
     * @param name
     * @param data
     */
    const registerEdge = (name, data) => {
        registerWorkflowEdge(name, data)
    }

    /**
     * 新建事件节点
     * @param {Any} data.nodeConfig 节点的配置数据
     * @param {Any} data.edgeConfig 链接线的配置数据
     * @param {Any} data.data 节点需要更新的数据
     * @param {Any} data.from 关联的 from 节点 ID
     * @param {Any} data.to 关联的 to 节点 ID
     * @param {Boolean} data.toEnd 是否关联到结束节点
     */
    const appendEvent = ({
        nodeConfig = {},
        edgeConfig = {},
        data,
        from = [],
        to = [],
        toEnd = false
    }) => {
        console.log('appendEvent', data)
        getWorkflowCls((cls) => {
            const node = cls.appendNode(nodeConfig)

            node.setData(data)

            // 关联起点和终点
            cls.assignNodes(from, node.id, edgeConfig.from || {})
            cls.assignNodes(node.id, to, edgeConfig.to || {})

            if (toEnd && !to?.length) {
                nodeToEnd(node.id)
            }

            cls.layout()
        })
    }

    /**
     * 更新事件节点
     * @param {Any} data.nodeConfig 节点的配置数据
     * @param {Any} data.edgeConfig 链接线的配置数据
     * @param {Any} data.data 节点需要更新的数据
     */
    const modifyEvent = ({ id, data }) => {
        getWorkflowCls((cls) => {
            const node = cls.getNode(id)

            if (!node) {
                return
            }

            node.setData(data)

            cls.layout()
        })
    }

    /**
     * 获取另一个判断节点
     * @param {TNodeID} parentID
     * @returns
     */
    const getOtherJudgmentNode = (id) => {
        return getWorkflowCls((cls) => {
            const node = cls.getExtraNode(id)
            const parentToIds = cls.getToIds(node?.from?.[0])

            const otherID = parentToIds?.find((_id) => _id !== id)

            if (!otherID) {
                return
            }

            return cls.getNode(otherID)
        })
    }

    /**
     * 新建判断事件节点
     * @param {String} data.data 判断事件的内容
     * @param {Any} data.trueData 通过后的事件数据
     * @param {Any} data.falseData 未通过的事件数据
     */
    const appendJudgment = ({
        trueData = {},
        falseData = {},
        data,
        from,
        to
    }) => {
        console.log(
            '🚀 ~ appendJudgment ~ trueData = {}, falseData = {}, data, from, to:',
            trueData,
            falseData,
            data,
            from,
            to
        )
        getWorkflowCls((cls) => {
            cls.removeEdges(from, to)

            const node = cls.appendNode()

            node.setData({
                module: enumEventModule.judgment,
                title: '审核节点',
                content: data.content
            })

            cls.assignNodes(from, node.id)

            // 创建通过的节点
            appendEvent({
                data: {
                    module: enumEventModule.judgmentValid,
                    event: trueData.event,
                    category: trueData.category,
                    icon: trueData.icon,
                    title: trueData.title,
                    content: trueData.content
                },
                edgeConfig: {
                    from: {
                        shape: 'edge',
                        ...cls.edgeValidAttrs
                    }
                },
                from: node.id,
                to: to
            })

            // 创建未通过的节点
            if (falseData.category === 'end') {
                nodeToEnd(node.id, cls.edgeInvalidAttrs)
            } else {
                // 创建未通过的节点
                appendEvent({
                    data: {
                        module: enumEventModule.judgmentInvalid,
                        category: falseData.category,
                        event: falseData.event,
                        icon: falseData.icon,
                        title: falseData.title,
                        content: falseData.content
                    },
                    edgeConfig: {
                        from: {
                            shape: 'edge',
                            ...cls.edgeInvalidAttrs
                        }
                    },
                    from: node.id
                })
            }

            cls.layout()
        })
    }

    /**
     * 更新判断事件节点
     * @param {String} data.data 判断事件的内容
     */
    const modifyJudgment = ({ id, data }) => {
        getWorkflowCls((cls) => {
            const node = cls.getNode(id)

            node.setData({
                content: data.content
            })

            cls.layout()
        })
    }

    /**
     * 删除判断事件的通过或未通过节点，只删除单个路径上的节点
     * @param {Boolean} resetModule  删除节点后是否重置判断结果节点的 module
     */
    const removeJudgment = (id, resetModule) => {
        getWorkflowCls((cls) => {
            const data = cls.getExtraNode(id)
            const parentID = data.data.module == 'judgment' ? id : data.from[0]
            const parentData = cls.getExtraNode(parentID)

            // 删除的是判断根节点
            if (parentID == id) {
                cls.removeNode(id, true)
            }
            // 删除的是判断结果的节点
            else {
                // 如果节点之后关联的是终点，则先断开与终点的连接线，防止产生多余的关联
                if (!data.to.some((_id) => _id !== cls.endID)) {
                    cls.removeEdges(data.id, data.to)
                }

                cls.removeNodes([parentID, id], true)
            }

            // 重置之前判断结果节点的 module
            if (resetModule) {
                parentData.to.forEach((toID) => {
                    cls.getNode(toID)?.setData({
                        module: enumEventModule.event
                    })
                })
            }
        })
    }

    /**
     * 把该节点标记为结束
     * @param id 节点 id
     */
    const nodeToEnd = (id, edgeConfig = {}) => {
        getWorkflowCls((cls) => {
            if (!cls.hasNode(cls.endID)) {
                cls.appendNode({
                    id: cls.endID,
                    shape: 'node-end'
                })
            }

            cls.assignNode(id, cls.endID, {
                shape: 'edge-end',
                ...edgeConfig
            })

            cls.layout()
        })
    }

    onBeforeUnmount(() => {
        workFlowCls?.destroy()
        workFlowCls = null

        destroyEvent()
        eventMap = null
    })

    provide(injectGetInstanceKey, getWorkflowCls)
    provide(injectGetGraphKey, getGraphCls)
    provide(injectEventOnKey, registerEvent)
    provide(injectEventEmitKey, emitEvent)
    provide(injectAppendEventKey, appendEvent)
    provide(injectModifyEventKey, modifyEvent)
    provide(injectAppendJudgmentKey, appendJudgment)
    provide(injectModifyJudgmentKey, modifyJudgment)

    return {
        refSketchpad,
        registerNode,
        registerEdge,
        startID,
        endID,
        getWorkflowCls,
        getGraphCls,
        appendEvent,
        modifyEvent,
        getOtherJudgmentNode,
        appendJudgment,
        modifyJudgment,
        removeJudgment,
        nodeToEnd,
        registerEvent,
        emitEvent
    }
}

export function useInject() {
    const getWorkflowCls = inject(injectGetInstanceKey, () => {})
    const getGraphCls = inject(injectGetGraphKey, () => {})
    const registerEvent = inject(injectEventOnKey, (...args) => {})
    const emitEvent = inject(injectEventEmitKey, (...args) => {})
    const appendEvent = inject(
        injectAppendEventKey,
        ({ nodeConfig = {}, edgeConfig = {}, data, from, to }) => {}
    )
    const modifyEvent = inject(injectModifyEventKey, ({ id, data }) => {})
    const appendJudgment = inject(
        injectAppendJudgmentKey,
        ({ trueData = {}, falseData = {}, data, from, to }) => {}
    )
    const modifyJudgment = inject(injectModifyJudgmentKey, ({ id, data }) => {})

    return {
        getWorkflowCls,
        getGraphCls,
        registerEvent,
        emitEvent,
        appendEvent,
        modifyEvent,
        appendJudgment,
        modifyJudgment
    }
}

/**
 * 注册 x6 起始节点
 */
export function registerStartNode(callback) {
    registerWorkflowNode('node-start', {
        inherit: 'circle',
        width: 60,
        height: 60,
        attrs: {
            body: {
                strokeWidth: 2,
                stroke: '#5F95FF',
                fill: '#FFF'
            },
            text: {
                text: '开始',
                fontSize: 12,
                fontWeight: 500
            }
        },
        tools: [
            {
                name: 'button',
                args: {
                    markup: [
                        {
                            tagName: 'circle',
                            selector: 'button',
                            attrs: {
                                r: '30',
                                fill: 'transparent',
                                // fill: '#000',
                                cursor: 'pointer'
                            }
                        }
                    ],
                    x: '50%',
                    y: '50%',
                    onClick(data) {
                        callback?.(data)
                    }
                }
            }
        ]
    })
}

/**
 * 注册 x6 终结节点
 */
export function registerEndNode() {
    registerWorkflowNode('node-end', {
        inherit: 'circle',
        width: 60,
        height: 60,
        attrs: {
            body: {
                strokeWidth: 4,
                stroke: '#5F95FF',
                fill: '#FFF'
            },
            text: {
                text: '结束',
                fontSize: 12,
                fontWeight: 500
            }
        }
    })
}

/**
 * 注册 x6 连接线
 */
export function registerConnectorEdge(callback) {
    registerWorkflowEdge('edge-connector', {
        tools: [
            {
                name: 'button',
                args: {
                    markup: [
                        {
                            tagName: 'circle',
                            selector: 'button',
                            attrs: {
                                r: 18,
                                stroke: '#5F95FF',
                                strokeWidth: 2,
                                fill: 'white',
                                cursor: 'pointer'
                            }
                        },
                        {
                            tagName: 'text',
                            textContent: '添加',
                            selector: 'icon',
                            attrs: {
                                fill: '#5F95FF',
                                fontSize: 10,
                                textAnchor: 'middle',
                                pointerEvents: 'none',
                                y: '0.3em'
                            }
                        }
                    ],
                    distance: '50%',
                    onClick(data) {
                        callback?.(data)
                    }
                }
            }
        ]
    })
}

/**
 * 注册 x6 关联终点的连接线
 */
export function registerEndEdge(callback) {
    registerWorkflowEdge('edge-end', {
        tools: [
            {
                name: 'button',
                args: {
                    markup: [
                        {
                            tagName: 'circle',
                            selector: 'button',
                            attrs: {
                                r: 18,
                                stroke: '#F16547',
                                strokeWidth: 2,
                                fill: 'white',
                                cursor: 'pointer'
                            }
                        },
                        {
                            tagName: 'text',
                            textContent: '删除',
                            selector: 'icon',
                            attrs: {
                                fill: '#F16547',
                                fontSize: 10,
                                textAnchor: 'middle',
                                pointerEvents: 'none',
                                y: '0.3em'
                            }
                        }
                    ],
                    distance: '50%',
                    onClick(data) {
                        callback?.(data)
                    }
                }
            }
        ]
    })
}
